
import os
import re
import tkinter as tk
from tkinter import filedialog, messagebox
from pathlib import Path

class JsToTsConverter:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("JS转TS转换器")
        self.root.geometry("650x400")
        self.setup_ui()
    
    def setup_ui(self):
        # 主框架
        main_frame = tk.Frame(self.root, padx=20, pady=10)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 转换模式选择
        mode_frame = tk.Frame(main_frame)
        mode_frame.pack(fill=tk.X, pady=5)
        tk.Label(mode_frame, text="转换模式:", font=("Arial", 9, "bold")).pack(anchor="w")
        
        self.convert_mode = tk.StringVar(value="folder")
        tk.Radiobutton(mode_frame, text="批量转换文件夹", 
                      variable=self.convert_mode, value="folder").pack(side=tk.LEFT)
        tk.Radiobutton(mode_frame, text="转换单个文件", 
                      variable=self.convert_mode, value="file").pack(side=tk.LEFT)
        
        # 输入路径
        input_frame = tk.Frame(main_frame)
        input_frame.pack(fill=tk.X, pady=5)
        tk.Label(input_frame, text="JS文件/目录:").pack(anchor="w")
        self.input_entry = tk.Entry(input_frame)
        self.input_entry.pack(fill=tk.X, padx=5)
        tk.Button(input_frame, text="浏览...", command=self.select_input).pack(pady=5)
        
        # 输出路径
        output_frame = tk.Frame(main_frame)
        output_frame.pack(fill=tk.X, pady=5)
        tk.Label(output_frame, text="TS输出目录:").pack(anchor="w")
        self.output_entry = tk.Entry(output_frame)
        self.output_entry.pack(fill=tk.X, padx=5)
        tk.Button(output_frame, text="浏览...", command=self.select_output).pack(pady=5)
        
        # 转换选项
        option_frame = tk.Frame(main_frame)
        option_frame.pack(fill=tk.X, pady=5)
        tk.Label(option_frame, text="转换选项:", font=("Arial", 9, "bold")).pack(anchor="w")
        
        self.keep_comments = tk.BooleanVar(value=True)
        tk.Checkbutton(option_frame, text="保留注释", 
                      variable=self.keep_comments).pack(side=tk.LEFT)
        
        self.infer_types = tk.BooleanVar(value=True)
        tk.Checkbutton(option_frame, text="自动推断类型", 
                      variable=self.infer_types).pack(side=tk.LEFT)
        
        # 转换按钮
        tk.Button(main_frame, text="开始转换", command=self.start_conversion).pack(pady=10)
    
    def select_input(self):
        if self.convert_mode.get() == "folder":
            path = filedialog.askdirectory(title="选择JS文件夹")
        else:
            path = filedialog.askopenfilename(title="选择JS文件", 
                filetypes=[("JavaScript Files", "*.js")])
        if path:
            self.input_entry.delete(0, tk.END)
            self.input_entry.insert(0, path)
    
    def select_output(self):
        path = filedialog.askdirectory(title="选择TS输出目录")
        if path:
            self.output_entry.delete(0, tk.END)
            self.output_entry.insert(0, path)
    
    def start_conversion(self):
        input_path = self.input_entry.get()
        output_path = self.output_entry.get()
        
        if not input_path or not output_path:
            messagebox.showerror("错误", "请选择输入和输出路径")
            return
        
        try:
            files = []
            if self.convert_mode.get() == "folder":
                for js_file in Path(input_path).glob("*.js"):
                    files.append(js_file)
            else:
                files.append(Path(input_path))
            
            success_count = 0
            for js_file in files:
                if self.convert_file(js_file, output_path):
                    success_count += 1
            
            messagebox.showinfo("完成", f"成功转换 {success_count}/{len(files)} 个文件")
        except Exception as e:
            messagebox.showerror("错误", f"转换失败: {str(e)}")
    
    def convert_file(self, js_file, output_dir):
        try:
            with open(js_file, 'r', encoding='utf-8') as f:
                js_content = f.read()
            
            # 转换内容
            ts_content = self.convert_js_to_ts(js_content, js_file.stem)
            
            # 写入文件
            output_path = Path(output_dir) / f"{js_file.stem}.ts"
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write(ts_content)
            return True
        except Exception as e:
            print(f"转换 {js_file} 失败: {str(e)}")
            return False
    
    def convert_js_to_ts(self, js_code, module_name):
        # 保留注释
        if not self.keep_comments.get():
            js_code = re.sub(r'//.*?\n|/\*.*?\*/', '', js_code, flags=re.DOTALL)
        
        # 转换变量声明
        js_code = self.convert_variables(js_code)
        
        # 转换函数声明
        js_code = self.convert_functions(js_code)
        
        # 转换类声明
        js_code = self.convert_classes(js_code)
        
        # 添加模块导出
        if "export default" not in js_code and "module.exports" not in js_code:
            js_code += f"\n\nexport default {module_name};"
        
        # 转换CommonJS导出
        js_code = js_code.replace("module.exports =", "export default")
        
        return f"// 自动从JS转换的TypeScript文件\n{js_code}"
    
    def convert_variables(self, code):
        # 转换var为let/const
        code = re.sub(r'\bvar\b', 'let', code)
        
        # 自动推断类型
        if self.infer_types.get():
            # 匹配变量声明
            for match in re.finditer(r'let\s+(\w+)\s*=\s*(.+?)(;|$)', code):
                var_name, value, _ = match.groups()
                var_type = self.infer_value_type(value)
                if var_type != "any":
                    code = code.replace(match.group(0), 
                                      f"let {var_name}: {var_type} = {value};")
        return code
    
    def convert_functions(self, code):
        # 转换函数参数和返回值类型
        if self.infer_types.get():
            # 普通函数
            code = re.sub(r'function\s+(\w+)\s*\(([^)]*)\)', 
                         self.add_function_types, code)
            # 箭头函数
            code = re.sub(r'const\s+(\w+)\s*=\s*\(([^)]*)\)\s*=>', 
                         self.add_arrow_function_types, code)
        return code
    
    def convert_classes(self, code):
        # 转换类属性和方法
        if self.infer_types.get():
            # 类属性
            code = re.sub(r'this\.(\w+)\s*=\s*(.+?);', 
                         self.add_class_property_types, code)
            # 类方法
            code = re.sub(r'(\w+)\(([^)]*)\)\s*{', 
                         self.add_method_types, code)
        return code
    
    def add_function_types(self, match):
        name, params = match.groups()
        typed_params = []
        for param in params.split(','):
            param = param.strip()
            if not param:
                continue
            if '=' in param:
                pname, pval = param.split('=', 1)
                ptype = self.infer_value_type(pval.strip())
                typed_params.append(f"{pname.strip()}: {ptype} = {pval.strip()}")
            else:
                typed_params.append(f"{param}: any")
        return f"function {name}({', '.join(typed_params)}): any"
    
    def add_arrow_function_types(self, match):
        name, params = match.groups()
        typed_params = []
        for param in params.split(','):
            param = param.strip()
            if not param:
                continue
            if '=' in param:
                pname, pval = param.split('=', 1)
                ptype = self.infer_value_type(pval.strip())
                typed_params.append(f"{pname.strip()}: {ptype} = {pval.strip()}")
            else:
                typed_params.append(f"{param}: any")
        return f"const {name} = ({', '.join(typed_params)}): any =>"
    
    def add_class_property_types(self, match):
        prop, value = match.groups()
        prop_type = self.infer_value_type(value)
        return f"this.{prop}: {prop_type} = {value};"
    
    def add_method_types(self, match):
        name, params = match.groups()
        typed_params = []
        for param in params.split(','):
            param = param.strip()
            if not param:
                continue
            typed_params.append(f"{param}: any")
        return f"{name}({', '.join(typed_params)}): any {{"
    
    def infer_value_type(self, value):
        if re.match(r'^\d+\.?\d*$', value):
            return "number"
        elif value in ('true', 'false'):
            return "boolean"
        elif value.startswith('"') or value.startswith("'"):
            return "string"
        elif value.startswith('['):
            return "any[]"
        elif value.startswith('{'):
            return "object"
        elif value.startswith('new '):
            return value.split('(')[0][4:]
        return "any"

if __name__ == "__main__":
    converter = JsToTsConverter()
    converter.root.mainloop()
